DYNAMIC UPDATE OF CLASSIC DESK ACCESSORIES
by Richard Bennett
Copyright (c) 1990 Apple Users' Group, Sydney
Republished from Applecations, a publication of the Apple Users' Group, Sydney, Australia.


If you've ever written a CDA, you'll know how frustrating it is to reboot after each assembly, just to test the darn thing. If the CDA gets fairly big, development starts to slow down quite considerably. What is needed, is a way to re-install CDAs dynamically. Also, considering that it's a CDA, you could then test, debug, and re-assemble the entire thing without ever leaving the assembler.

There are currently NDAs available to re-install NDAs, but nothing for CDAs. With the release of System 5.0 however, the job of dynamic re-installs becomes fairly easy.

First off, we have to consider a few posing questions;

1 - How do we invoke the re-install?
2 - What tools and environment are necessary for the re-install?
3 - How do we perform the re-install?

The first is easy. Simply install an option into the CDA to re-install itself. When that option is selected, the CDA should reload itself from disk, install it into the CDA menu, and remove the current entry.

For the second, we'll obviously require GS/OS, and the Desk Manager to perform the CDA manipulations. We may also need the Memory Manager, and obviously the System Loader.

How do we do it? There are a few problems here. The first, is that we have no idea who we are, pathname wise. This is where the System Loader comes in handy, as the tool call _LGetPathname2 will return a GS/OS pathname when supplied with the UserID (_LGetPathname will also do, but will only return a class 0 string pathname). This is great, but how do we get the UserID? The original UserID that the System Loader used to load the CDA, is unknown to the CDA at the moment, but the easiest way to get it is as follows;

FindMyUserID    PHA                      ;Push some space
                PHA
                PushLong #FindMyUserID   ;Push my address on the stack
                _FindHandle              ;Find my handle (allocated by the
                                         ; System Loader on bootup)
                PullLong  0              ;Retrieve my handle
                LDY       #6             ;Offset in HandleRec of UserID
                LDA       [0],Y          ;Get my UserID
                STA       MyID
                RTS

This is necessary, as all memory allocations the CDA does, are allocated by the UserID obtained from the startup. eg. If your CDA requires a UserID to allocate memory, you should do the following;

GetNewUserID    PHA                      ;Push some space
                _MMStartup               ;Get a new Master ID
                PLA                      ;Retrieve it
                STA       UserID         ;Save it
                ORA       #$100          ;Make a new Aux ID
                STA       AllocID        ;Use it for allocating memory
                RTS

Now, we've got the UserID, all we need now is the pathname;

FindMyPath      PHA                      ;Some space
                PHA
                PEI       MyID           ;First parameter is UserID to find
                PEA       0              ;Next is the segment number
                _LGetPathname2
                PullLong  PathAdr        ;Retrieve the address of the path
                RTS

Ok, now to load the file. To do this, we call _InitialLoad2, which loads the file and returns with it's address, which we can find the handle for, and use it to install it in the CDA menu by using _InstallCDA.

The next step, is to discard myself. To start off, we should dispose of any memory we've allocated privately. Now, the hard part is to dispose of myself, delete my UserID, discard my entry in the System Loader pathname table, and either return to the CDA menu, or call the new CDA directly (we have the pointer to it remember!). The call to do all this, is _UserShutdown.

The Memory Manager won't move or purge any memory if the system is currently running under an interrupt request. This includes the Desk Accessory menu. So theoretically, we can simply dispose of ourself, and THEN either return, or call the new CDA. Unfortunately, it isn't that simple. If, in future versions of the system software, the _UserShutdown call decides to allocate any memory (for work areas such as re-building the pathname table, or the Memory Manager re-building the UserID list), and the CDA memory was already purged, and memory is almost full, the allocations it makes could overwrite where I was calling from. When the System Loader returns, I may not necessarily be still there! Also, executing code from a purged memory segment isn't very clever programming. A way around this, is to call _UserShutdown from somewhere in bank 0 (say at 00/0200) where the Memory Manager can't touch you, or simply play the odds (like I do) that the above will never happen (after all, it's only a debugging feature that'll be removed when you've finished writing it!).

So, to summarize, these are the steps to follow;

1 - Get the UserID that the CDA was loaded with (_FindHandle).
2 - Get my pathname from the System Loader (_LGetpathname2).
3 - Call _InitialLoad2 to load myself from disk.
4 - Install it into the CDA menu (_InstallCDA).
5 - Remove myself from the CDA menu (_RemoveCDA).
6 - Dispose all of the memory that I've allocated privately.
7 - Call _UserShutdown to dispose of everything, and optionally call the new CDA.

The entire code (in Merlin format, WITHOUT supermacros) follows;

ReloadCDA       PHA                      ;Find me
                PHA
                PushLong  #*
                _FindHandle
                PullLong  MyHandle
                LDY       #6
                LDA       [MyHandle],Y
                STA       MyID
                PHA                      ;Push space for _InitialLoad2
                PHA
                PHA
                PHA
                PHA
                PEA       $5000          ;UserID type for loaded file
                PHA                      ;Push space and UserID for _LGet
                PHA
                PHA
                PEA       0              ;Segment number
                _LGetPathname2           ;Leave results on stack
                PEA       $FFFF          ;Don't use special memory
                PEA       1              ;Loader type 1
                _InitialLoad2            ;Load the CDA
                PLA                      ;Get the UserID assigned
                _FindHandle              ;Address and DP/S on stack still
                _InstallCDA              ;Install the CDA
                PushLong  MyHandle       ;Now remove myself from the menu
                _RemoveCDA
                PHA                      ;Space for _UserShutdown
                PEI       MyID           ;Shutdown who?
                PEA       0              ;Kill everything!
                _UserShutdown
                PLA                      ;Remove the UserID
                RTL                      ;Return to Desk Manager
                                         ;(normal CDA quit)

Here is the list of tool calls used;

_FindHandle     $1A03     Memory Manager
_LGetPathname2  $2211     System Loader
_InitialLoad2   $2011     System Loader
_InstallCDA     $0F05     Desk Manager
_RemoveCDA      $2105     Desk Manager
_UserShutdown   $1211     System Loader


THIS CONTENT COPYRIGHT © 2007, APPLE MACINTOSH USERS' GROUP, SYDNEY
Permission has been obtained to make this material available on the Internet.

Permission is hereby granted for non-profit user groups to republish this content.
PLEASE CREDIT THE AUTHOR AND THE SOURCE: Applecations, publication of the Apple Users' Group, Sydney, Australia

THIS PAGE COPYRIGHT © 2007, ANDREW ROUGHAN